//
// Created by 邹嘉旭 on 2025/8/12.
//

#include "as_entity.h"
#include "define.h"
#include "interface/air.h"
#include "interface/ground.h"
#include "interface/gs1.h"

static l_err recv_g5_as_auz_info(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt);
static l_err recv_g5_bearer_est_rqst(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt);
static l_err recv_g5_bearer_est_resp(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt);
static l_err recv_g5_as_st_chg(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt);
static l_err recv_g5_as_exit(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt);
static l_err recv_g5_path_switch_request(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt);
static l_err recv_g5_path_switch_response(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt);

size_t g5_format_size = 7;
cn_format_desc_t g5_format_descs[] = {
    {G5_AS_AUZ_INFO, &g3_g5_as_auz_info_desc, "G3 AS AUZ INFO", sizeof(g3_g5_as_auz_info_t), 4, NULL, recv_g5_as_auz_info, default_cn_forward_handler, free},
    {G5_DATA_BEARER_EST_RQST, &g1_g5_bearer_est_desc, "G5 BEARER EST RQST", sizeof(g1_g5_bearer_est_t), 0, default_cn_send_handler, recv_g5_bearer_est_rqst, NULL, free_g1_g5_bearer_est},
    {G5_DATA_BEARER_EST_RESP, &g1_g5_bearer_est_desc, "G5 BEARER EST RESP", sizeof(g1_g5_bearer_est_t), 0, NULL, recv_g5_bearer_est_resp, default_cn_forward_handler, free_g1_g5_bearer_est},
    {G5_AS_STATE_CHANGE, &g1_g3_g5_as_st_chg_desc, "G3 AS STATE CHANGE", sizeof(g1_g3_g5_as_st_chg_t), 5, default_cn_send_handler, recv_g5_as_st_chg, default_cn_forward_handler, free},
    {G5_AS_EXIT, &g1_g5_as_release_exit_desc, "G5 AS EXIT", sizeof(g1_g5_as_release_exit_t), 3, NULL, recv_g5_as_exit, default_cn_forward_handler, free},
    {G5_PATH_SWITCH_REQUEST, &g1_g5_path_switch_desc, "G5 PATH SWITCH REQUEST", sizeof(g1_g5_path_switch_t), 21, NULL, recv_g5_path_switch_request, default_cn_forward_handler, free_g1_g5_path_switch},
    {G5_PATH_SWITCH_RESPONSE, &g1_g5_path_switch_desc, "G5 PATH SWITCH RESPONSE", sizeof(g1_g5_path_switch_t), 21, default_cn_send_handler, recv_g5_path_switch_response, default_cn_forward_handler, free_g1_g5_path_switch},
};
static l_err recv_g5_as_auz_info(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt) {
    g3_g5_as_auz_info_t *auz = NULL;
    if ((auz = parse_sdu(in_buf, desc->f_desc, desc->struct_size)) == NULL) {
        log_error("Parse SDU failed!");
        if (auz)    desc->free_func(auz);
        return LD_ERR_INTERNAL;
    }

    if (!has_as_senode(net_ele->as_map, auz->AS_SAC)) {
        as_entity_t *as = init_as_entity(0, auz->AS_SAC, propt->peer_sac, ELEMENT_DBSM, net_ele);
        // 设定AS当前接口的回复propt
        // set_as_propt(as, INTERFACE_G5, propt);

        if (change_state(&as->dbsm_sm, EV_DEFAULT,
                         &(fsm_event_data_t){
                             &dbsm_fsm_events[DBSM_CONNECTING], NULL
                         })) {
            cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "cant change state correctly");
            desc->free_func(auz);
            return LD_ERR_INVALID;
        }

        set_as_enode(net_ele->as_map, as);

        g1_g5_bearer_est_t pkt = {
            .GTYPE = G5_DATA_BEARER_EST_RQST,
            .VERSION = CN_VERSION,
            .AS_SAC = auz->AS_SAC,
            .element_type = 0xF,
            .snf_msg = init_buffer_unptr(),
            .IP = init_buffer_unptr(),
            .port = 50001,
        };
        struct in6_addr addr;
        inet_pton(AF_INET6, "::1", &addr);

        sn_session_est_rqst_t est_rqst = {
            .SN_TYP = SN_SESSION_EST_RQST,
            .VER = CN_VERSION,
            .PID = 1,
            .AS_SAC = auz->AS_SAC,
            .SER_TYPE = 0xF,
        };
        buffer_t *snf_msg_buf = gen_pdu(&est_rqst, &sn_session_est_rqst_desc, "SN SESSION EST RQST");

        // cn_log_buf(LOG_WARN, net_ele->element_tag, INTERFACE_G5, "EST RQST", snf_msg_buf->ptr, snf_msg_buf->len);

        CLONE_TO_CHUNK(*pkt.snf_msg, snf_msg_buf->ptr, snf_msg_buf->len);

        CLONE_TO_CHUNK(*pkt.IP, addr.__in6_u.__u6_addr8, 16);

        free_buffer(snf_msg_buf);

        // buffer_t *bearer = gen_pdu(&pkt, &g1_g5_bearer_est_rqst_desc, "G1 G5 BEARER EST RQST");

        // cn_log_buf(LOG_WARN, net_ele->element_tag, INTERFACE_G5, "BEARER", bearer->ptr, bearer->len);

        cn_format_desc_t *send_desc = get_non_sequential_desc(g5_format_size, g5_format_descs, G5_DATA_BEARER_EST_RQST);
        if (!send_desc || !send_desc->send_handler || send_desc->send_handler(
                net_ele,
                &pkt,
                send_desc,
                get_propt(net_ele, INTERFACE_G5, as->current_client_peer_sac),
                INTERFACE_G5
            ) != LD_OK) {
            cn_log_error(net_ele->element_tag, INTERFACE_G1, "Failed to send");
            free_buffer(pkt.snf_msg);
            free_buffer(pkt.IP);
            desc->free_func(auz);
            return LD_ERR_NULL;
            }

        free_buffer(pkt.snf_msg);
        free_buffer(pkt.IP);
        desc->free_func(auz);
        return LD_OK;
    }
    cn_log_error(net_ele->element_tag, INTERFACE_G5, "Can not repeatly access AS.");
    desc->free_func(auz);
    return LD_ERR_INVALID;
}

static l_err recv_g5_bearer_est_rqst(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt) {
    g1_g5_bearer_est_t *bearer = calloc(1, sizeof(g1_g5_bearer_est_t));
    PARSE_DSTR_PKT(in_buf, bearer, snf_msg, g1_g5_bearer_est_desc, G1_G3_G5_PKT_HEAD_LEN, 0)

    as_entity_t *as = get_as_uenode_by_SAC(net_ele->as_map, bearer->AS_SAC);

    if (!as) {
        desc->free_func(bearer);
        cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "No such AS, which SAC = %d", bearer->AS_SAC);
        return LD_ERR_INTERNAL;
    }
    cn_format_desc_t *send_desc = get_non_sequential_desc(g1_format_size, g1_format_descs, G1_DATA_BEARER_EST_RQST);
    if (!send_desc || !send_desc->forward_handler || send_desc->forward_handler(
            net_ele,
            in_buf,
            get_propt(net_ele, INTERFACE_G1, as->current_client_peer_sac),
            INTERFACE_G1
        ) != LD_OK) {
        cn_log_error(net_ele->element_tag, INTERFACE_G1, "Failed to send");
        desc->free_func(bearer);
        return LD_ERR_NULL;
        }

    desc->free_func(bearer);
    return LD_OK;
}

static l_err recv_g5_bearer_est_resp(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt) {
    g1_g5_bearer_est_t *bearer = calloc(1, sizeof(g1_g5_bearer_est_t));
    // bearer->IP = init_buffer_ptr((GEN_ADDRLEN));
    PARSE_DSTR_PKT(in_buf, bearer, snf_msg, g1_g5_bearer_est_desc, G1_G3_G5_PKT_HEAD_LEN, (GEN_ADDRLEN) + 2)

    sn_session_est_resp_t *est_resp = parse_sdu(bearer->snf_msg, &sn_session_est_resp_desc, sizeof(sn_session_est_resp_t));

    as_entity_t *as = get_as_senode_by_SAC(net_ele->as_map, bearer->AS_SAC);
    if (!as) {
        desc->free_func(bearer);
        cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "No such AS, which SAC = %d", bearer->AS_SAC);
        return LD_ERR_INTERNAL;
    }
    set_as_as_ip(as, (char *)est_resp->IP_AS->ptr);
    set_as_gs_ip(as, (char *)bearer->IP->ptr, bearer->port);

    g7_bearer_command_type1_t type1 = {
        .IP_AS = init_buffer_unptr(),
        .IP_GS = init_buffer_unptr(),
        .port_gs = bearer->port,
    };
    CLONE_TO_CHUNK(*type1.IP_AS, est_resp->IP_AS->ptr, GEN_ADDRLEN);
    CLONE_TO_CHUNK(*type1.IP_GS, bearer->IP->ptr, GEN_ADDRLEN);

    g7_bearer_command_t cmd = {
        .GTYPE = G7_DATA_BEARER_SESSION_COMMAND,
        .VERSION = CN_VERSION,
        .AS_SAC = bearer->AS_SAC,
        .CMD_TYPE = ACCESS,
        .SERVICE = 0xF,
        .sdu = gen_pdu(&type1, &g7_bearer_command_type1_desc, "G7 BEARER CMD TYPE1"),
    };

    cn_format_desc_t *send_desc = get_non_sequential_desc(g7_format_size, g7_format_descs, G7_DATA_BEARER_SESSION_COMMAND);
    if (!send_desc || !send_desc->send_handler || send_desc->send_handler(
            net_ele,
            &cmd,
            send_desc,
            get_propt(net_ele, INTERFACE_G7, 0),
            INTERFACE_G7
        ) != LD_OK) {
        cn_log_error(net_ele->element_tag, INTERFACE_G7, "Failed to send");
        free_buffer(type1.IP_GS);
        free_buffer(type1.IP_AS);
        free_buffer(cmd.sdu);
        desc->free_func(bearer);
        return LD_ERR_NULL;
    }

    free_buffer(type1.IP_GS);
    free_buffer(type1.IP_AS);
    free_buffer(cmd.sdu);
    desc->free_func(bearer);
    free_sn_session_est_resp(est_resp);
    return LD_OK;
}

static l_err recv_g5_as_st_chg(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt) {

    g1_g3_g5_as_st_chg_t *chg = NULL;
    if ((chg = parse_sdu(in_buf, desc->f_desc, desc->struct_size)) == NULL) {
        log_error("Parse SDU failed!");
        if (chg)    desc->free_func(chg);
        return LD_ERR_INVALID;
    }

    switch (chg->STATE) {
        case ACCESS:
        case EXIT:{
            as_entity_t *as = get_as_uenode_by_SAC(net_ele->as_map, chg->AS_SAC);

            if (!as) {
                desc->free_func(chg);
                cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "No such AS, which SAC = %d", chg->AS_SAC);
                return LD_ERR_INVALID;
            }

            switch (chg->STATE) {
                case ACCESS:
                    if (change_state(&as->amm_sm, EV_DEFAULT,
                                     &(fsm_event_data_t){
                                         &amm_fsm_events[AMM_CONNECTED], NULL})) {
                        cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "cant change state correctly");
                        desc->free_func(exit);
                        return LD_ERR_INVALID;
                    }
                    break;
                case EXIT:
                    // 发送释放AS

                    g1_g5_as_release_exit_t release = {
                        .GTYPE = G1_RELEASE_AS_CONTEXT,
                        .VERSION = CN_VERSION,
                        .AS_SAC = as->SAC,
                    };

                    cn_format_desc_t *send_desc = get_non_sequential_desc(g1_format_size, g1_format_descs, G1_RELEASE_AS_CONTEXT);
                    if (!send_desc || !send_desc->send_handler || send_desc->send_handler(
                            net_ele,
                            &release,
                            send_desc,
                            get_propt(net_ele, INTERFACE_G1, as->current_client_peer_sac),
                            INTERFACE_G1
                        ) != LD_OK) {
                        cn_log_error(net_ele->element_tag, INTERFACE_G1, "Failed to send");
                        desc->free_func(chg);
                        return LD_ERR_NULL;
                        }


                    if (change_state(&as->amm_sm, EV_DEFAULT,
                                     &(fsm_event_data_t){
                                         &amm_fsm_events[AMM_EXIT], NULL})) {
                        cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "cant change state correctly");
                        desc->free_func(exit);
                        return LD_ERR_INVALID;
                                         }

                    if (delete_element_uenode_by_SAC(net_ele->as_map, as->SAC) != LD_OK) {
                        cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "Failed to delete as entity");
                        desc->free_func(chg);
                        return LD_ERR_INVALID;
                    }

                default:
                    break;
            }

            desc->free_func(chg);
            break;
        }
        case SWITCH: {
            if (net_ele->element_type == ELEMENT_DBSM) {
                as_entity_t *as = get_as_senode_by_SAC(net_ele->as_map, chg->AS_SAC);

                if (!as) {
                    desc->free_func(chg);
                    cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "No such AS, which SAC = %d", chg->AS_SAC);
                    return LD_ERR_INVALID;
                }

                // 这里的as的IP_GS和port 是在path switch request记录的
                g7_bearer_command_type2_t type2 = {
                    .IP_GS = init_buffer_unptr(),
                    .port_gs = as->port_GS,
                };
                CLONE_TO_CHUNK(*type2.IP_GS, as->IP_GS, GEN_ADDRLEN);

                // log_buf(LOG_FATAL, "!!!!!!!", as->IP_GS, GEN_ADDRLEN);
                // log_fatal("!!! %d", as->port_GS);
                //
                // struct in6_addr addr;
                // inet_pton(AF_INET6, "::1", &addr);

                g7_bearer_command_t cmd = {
                    .GTYPE = G7_DATA_BEARER_SESSION_COMMAND,
                    .VERSION = CN_VERSION,
                    .AS_SAC = chg->AS_SAC,
                    .CMD_TYPE = SWITCH,
                    .SERVICE = 0xF,
                    .sdu = gen_pdu(&type2, &g7_bearer_command_type2_desc, "G7 BEARER CMD TYPE2"),
                };

                cn_format_desc_t *send_desc = get_non_sequential_desc(g7_format_size, g7_format_descs, G7_DATA_BEARER_SESSION_COMMAND);
                if (!send_desc || !send_desc->send_handler || send_desc->send_handler(
                        net_ele,
                        &cmd,
                        send_desc,
                        get_propt(net_ele, INTERFACE_G7, 0),
                        INTERFACE_G7
                    ) != LD_OK) {
                    cn_log_error(net_ele->element_tag, INTERFACE_G7, "Failed to send");
                    free_buffer(type2.IP_GS);
                    free_buffer(cmd.sdu);
                    desc->free_func(chg);
                    return LD_ERR_NULL;
                }

                free_buffer(type2.IP_GS);
                free_buffer(cmd.sdu);
                return LD_OK;
            }else {

                // AMM
                as_entity_t *as = get_as_uenode_by_SAC(net_ele->as_map, chg->AS_SAC);

                if (!as) {
                    desc->free_func(chg);
                    cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "No such AS, which SAC = %d", chg->AS_SAC);
                    return LD_ERR_INVALID;
                }

                if (in_state(&as->amm_sm, "AMM_HO_EXECUTE")) {
                    if (change_state(&as->amm_sm, EV_DEFAULT,
                                     &(fsm_event_data_t){
                                         &amm_fsm_events[AMM_CONNECTED], NULL
                                     })) {
                        cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "cant change state correctly");
                        desc->free_func(chg);
                        return LD_ERR_INVALID;
                    }
                }else {
                    cn_log_warn(net_ele->element_tag, INTERFACE_G5, "Wrong status");
                }


                g1_g5_as_release_exit_t release = {
                    .GTYPE = G1_RELEASE_AS_CONTEXT,
                    .VERSION = CN_VERSION,
                    .AS_SAC = as->SAC,
                };

                cn_format_desc_t *send_desc = get_non_sequential_desc(g1_format_size, g1_format_descs, G1_RELEASE_AS_CONTEXT);
                if (!send_desc || !send_desc->send_handler || send_desc->send_handler(
                        net_ele,
                        &release,
                        send_desc,
                        get_propt(net_ele, INTERFACE_G1, as->current_client_peer_sac),
                        INTERFACE_G1
                    ) != LD_OK) {
                    cn_log_error(net_ele->element_tag, INTERFACE_G1, "Failed to send");
                    desc->free_func(chg);
                    return LD_ERR_NULL;
                }

                // 当前GS 已变成 切换后的目的GS
                as->current_client_peer_sac = as->next_peer_sac;
            }
            desc->free_func(chg);
            break;
        }
        default: {
            desc->free_func(chg);
            cn_log_error(net_ele->element_tag, INTERFACE_G7, "Invalid command type: %d", chg->STATE);
            return LD_ERR_INVALID;
        }
    }

    cn_format_desc_t *send_desc = get_non_sequential_desc(g3_format_size, g3_format_descs, G3_AS_STATE_CHANGE);
    if (!send_desc || !send_desc->forward_handler || send_desc->forward_handler(
            net_ele,
            in_buf,
            get_propt(net_ele, INTERFACE_G3, 0),
            INTERFACE_G3
        ) != LD_OK) {
        cn_log_error(net_ele->element_tag, INTERFACE_G3, "Failed to send");
        desc->free_func(chg);
        return LD_ERR_NULL;
        }


    return LD_OK;
}

static l_err recv_g5_as_exit(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt) {
    g1_g5_as_release_exit_t *exit = NULL;
    if ((exit = parse_sdu(in_buf, desc->f_desc, desc->struct_size)) == NULL) {
        log_error("Parse SDU failed!");
        if (exit)    desc->free_func(exit);
        return LD_ERR_INVALID;
    }

    as_entity_t *as = get_as_senode_by_SAC(net_ele->as_map, exit->AS_SAC);
    if (!as) {
        cn_log_error(net_ele->element_tag, INTERFACE_G5, "No such AS: %d.", exit->AS_SAC);
        desc->free_func(exit);
        return LD_ERR_INVALID;
    }

    g7_bearer_command_t cmd = {
        .GTYPE = G7_DATA_BEARER_SESSION_COMMAND,
        .VERSION = CN_VERSION,
        .AS_SAC = as->SAC,
        .CMD_TYPE = EXIT,
        .SERVICE = 0xF,
        .sdu = NULL,
    };

    cn_format_desc_t *send_desc = get_non_sequential_desc(g7_format_size, g7_format_descs, G7_DATA_BEARER_SESSION_COMMAND);
    if (!send_desc || !send_desc->send_handler || send_desc->send_handler(
            net_ele,
            &cmd,
            send_desc,
            get_propt(net_ele, INTERFACE_G7, 0),
            INTERFACE_G7
        ) != LD_OK) {
        cn_log_error(net_ele->element_tag, INTERFACE_G7, "Failed to send");
        desc->free_func(exit);
        return LD_ERR_NULL;
    }

    desc->free_func(exit);

    return LD_OK;
}

static l_err recv_g5_path_switch_request(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt) {
    g1_g5_path_switch_t *rqst = NULL;
    if ((rqst = parse_sdu(in_buf, desc->f_desc, desc->struct_size)) == NULL) {
        log_error("Parse SDU failed!");
        if (rqst)    desc->free_func(rqst);
        return LD_ERR_INVALID;
    }

    as_entity_t *as = get_as_senode_by_SAC(net_ele->as_map, rqst->AS_SAC);
    if (!as) {
        cn_log_error(net_ele->element_tag, INTERFACE_G5, "No such AS: %d.", rqst->AS_SAC);
        desc->free_func(rqst);
        return LD_ERR_INVALID;
    }

    set_as_gs_ip(as, (char *)rqst->IP->ptr, rqst->port);

    g1_g5_path_switch_t switch_response = {
        .GTYPE = G5_PATH_SWITCH_RESPONSE,
        .VERSION = CN_VERSION,
        .AS_SAC = as->SAC,
        // 不用RDF了，先换成as的IP
        .IP = init_buffer_unptr(),
        // 弃用
        .port = 0xC35B,
    };


    // struct in6_addr addr;
    // inet_pton(AF_INET6, "::1", &addr);
    // CLONE_TO_CHUNK(*switch_response.IP, addr.__in6_u.__u6_addr8, GEN_ADDRLEN);
    CLONE_TO_CHUNK(*switch_response.IP, as->IP_AS, GEN_ADDRLEN);

    cn_format_desc_t *send_desc_rqst = get_non_sequential_desc(g5_format_size, g5_format_descs,
                                                               G5_PATH_SWITCH_RESPONSE);

    if (!send_desc_rqst || !send_desc_rqst->send_handler || send_desc_rqst->send_handler(
            net_ele,
            &switch_response,
            send_desc_rqst,
            get_propt(net_ele, INTERFACE_G5, 0),
            INTERFACE_G5
        ) != LD_OK) {
        cn_log_error(net_ele->element_tag, INTERFACE_G5, "Failed to send");
        free_buffer(switch_response.IP);
        desc->free_func(rqst);
        return LD_ERR_NULL;
    }


    free_buffer(switch_response.IP);
    desc->free_func(rqst);
    return LD_OK;
}


static l_err recv_g5_path_switch_response(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt) {

    g1_g5_path_switch_t *rqst = NULL;
    if ((rqst = parse_sdu(in_buf, desc->f_desc, desc->struct_size)) == NULL) {
        log_error("Parse SDU failed!");
        if (rqst)    desc->free_func(rqst);
        return LD_ERR_INVALID;
    }

    as_entity_t *as = get_as_uenode_by_SAC(net_ele->as_map, rqst->AS_SAC);
    if (!as) {
        desc->free_func(rqst);
        cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "No such AS, which SAC = %d", rqst->AS_SAC);
        return LD_ERR_INVALID;
    }

    cn_format_desc_t *send_desc = get_non_sequential_desc(g1_format_size, g1_format_descs, G1_PATH_SWITCH_RESPONSE);
    if (!send_desc || !send_desc->forward_handler || send_desc->forward_handler(
            net_ele,
            in_buf,
            get_propt(net_ele, INTERFACE_G1, as->next_peer_sac),
            INTERFACE_G1
        ) != LD_OK) {
        cn_log_error(net_ele->element_tag, INTERFACE_G1, "Failed to send");
        desc->free_func(rqst);
        return LD_ERR_NULL;
        }

    desc->free_func(rqst);
    return LD_OK;
}
